/*
	路由管理模块，接口
	路由管理模块用于将消息Id与对应的Router方法匹配
	用于管理多个路由
*/

package zserver

import (
	"fmt"
	"zinx/utils"
	"zinx/zinterface"
)

type ZMsgHandler struct {
	Apis map[uint32]zinterface.IRouter

	// 0.8版本：消息队列
	TaskQueue []chan zinterface.IRequest

	// 0.8版本：用于处理消息队列的Worker个数
	WorkerPoolSize uint32
}

// 新建方法
func NewMsgHandler() zinterface.IMsgHandler {
	return &ZMsgHandler{
		Apis: make(map[uint32]zinterface.IRouter),

		TaskQueue: make([]chan zinterface.IRequest, utils.GlobalObject.WorkerPoolSize),
		WorkerPoolSize: utils.GlobalObject.WorkerPoolSize,
	}
}

// 实现IMsgHandler

// 为msgId添加路由
func (zmh *ZMsgHandler) AddRouter(msgId uint32, router zinterface.IRouter) {
	fmt.Println("start addrouter")
	
	// 判断当前msgId是否已经注册
	if _, ok := zmh.Apis[msgId]; ok {
		fmt.Println("addrouter error: msgId already exist")
		return
	}

	zmh.Apis[msgId] = router
}

// 调用路由
func (zmh *ZMsgHandler) DoRouter(request zinterface.IRequest) {
	fmt.Println("start dorouter")

	// 判断msgId是否已经注册
	router, ok := zmh.Apis[request.GetMsgId()]
	if !ok {
		fmt.Println("dorouter error: msgId ", request.GetMsgId(), " not registered. please addrouter first")
		return
	}

	fmt.Println("start handle request")

	// 调用router中前、中、后方法
	router.Handle(request)
	router.PostHandle(request)
	router.PreHandle(request)
}

// 开启一个工作池
func (zmh *ZMsgHandler) StartWorkPool() {
	// 通过循环创建WorkerPoolSize个协程
	for i := 0; i < int(zmh.WorkerPoolSize); i ++ {
		// 创建管道
		zmh.TaskQueue[i] = make(chan zinterface.IRequest, utils.GlobalObject.MaxTaskSize)
		
		// 调用StartOneWorker，创建协程
		go zmh.StartOneWorker(uint32(i), zmh.TaskQueue[i])

	}	
}

// 开启一个工作的协程
func (zmh *ZMsgHandler) StartOneWorker(workId uint32, taskQueue chan zinterface.IRequest) {
	fmt.Println("[worker pool]: start worker: ", workId)

	for request := range taskQueue {
		zmh.DoRouter(request)
	}
}

// 将request发送给对应的taskQueue
func (zmh *ZMsgHandler) SendMsgToTaskQueue(request zinterface.IRequest) {
	// 找到消息对应的taskqueue
	// TODO: 暂时用connId取模，会导致分配不平均的情况，需要改成使用requestId取模
	workerId := request.GetConnection().GetId() % zmh.WorkerPoolSize
	fmt.Println("send request.connid: ", request.GetConnection().GetId(), " to worker: ", workerId)

	// 将消息发送给消息队列
	zmh.TaskQueue[workerId] <- request
}
